/*
** $Id: lbitlib.c,v 1.30 2015/11/11 19:08:09 roberto Exp $
** Standard library for bitwise operations
** See Copyright Notice in lua.h
*/

#define lbitlib_c
#define LUA_LIB

#include "lprefix.h"


#include "lua.h"

#include "lauxlib.h"
#include "lualib.h"


#if defined(LUA_COMPAT_BITLIB)      /* { */


#define pushunsigned(L,n)   lua_pushinteger(L, (lua_Integer)(n))
#define checkunsigned(L,i)  ((lua_Unsigned)luaL_checkinteger(L,i))


/* number of bits to consider in a number */
#if !defined(LUA_NBITS)
#define LUA_NBITS   32
#endif


/*
** a lua_Unsigned with its first LUA_NBITS bits equal to 1. (Shift must
** be made in two parts to avoid problems when LUA_NBITS is equal to the
** number of bits in a lua_Unsigned.)
*/
#define ALLONES     (~(((~(lua_Unsigned)0) << (LUA_NBITS - 1)) << 1))


/* macro to trim extra bits */
#define trim(x)     ((x) & ALLONES)


/* builds a number with 'n' ones (1 <= n <= LUA_NBITS) */
#define mask(n)     (~((ALLONES << 1) << ((n) - 1)))



static lua_Unsigned andaux( lua_State* L ) {
    int i, n = lua_gettop( L );
    lua_Unsigned r = ~( lua_Unsigned )0;

    for ( i = 1; i <= n; i++ ) {
        r &= checkunsigned( L, i );
    }

    return trim( r );
}


static int b_and( lua_State* L ) {
    lua_Unsigned r = andaux( L );
    pushunsigned( L, r );
    return 1;
}


static int b_test( lua_State* L ) {
    lua_Unsigned r = andaux( L );
    lua_pushboolean( L, r != 0 );
    return 1;
}


static int b_or( lua_State* L ) {
    int i, n = lua_gettop( L );
    lua_Unsigned r = 0;

    for ( i = 1; i <= n; i++ ) {
        r |= checkunsigned( L, i );
    }

    pushunsigned( L, trim( r ) );
    return 1;
}


static int b_xor( lua_State* L ) {
    int i, n = lua_gettop( L );
    lua_Unsigned r = 0;

    for ( i = 1; i <= n; i++ ) {
        r ^= checkunsigned( L, i );
    }

    pushunsigned( L, trim( r ) );
    return 1;
}


static int b_not( lua_State* L ) {
    lua_Unsigned r = ~checkunsigned( L, 1 );
    pushunsigned( L, trim( r ) );
    return 1;
}


static int b_shift( lua_State* L, lua_Unsigned r, lua_Integer i ) {
    if ( i < 0 ) { /* shift right? */
        i = -i;
        r = trim( r );

        if ( i >= LUA_NBITS ) {
            r = 0;
        }
        else {
            r >>= i;
        }
    }
    else {   /* shift left */
        if ( i >= LUA_NBITS ) {
            r = 0;
        }
        else {
            r <<= i;
        }

        r = trim( r );
    }

    pushunsigned( L, r );
    return 1;
}


static int b_lshift( lua_State* L ) {
    return b_shift( L, checkunsigned( L, 1 ), luaL_checkinteger( L, 2 ) );
}


static int b_rshift( lua_State* L ) {
    return b_shift( L, checkunsigned( L, 1 ), -luaL_checkinteger( L, 2 ) );
}


static int b_arshift( lua_State* L ) {
    lua_Unsigned r = checkunsigned( L, 1 );
    lua_Integer i = luaL_checkinteger( L, 2 );

    if ( i < 0 || !( r & ( ( lua_Unsigned )1 << ( LUA_NBITS - 1 ) ) ) ) {
        return b_shift( L, r, -i );
    }
    else {  /* arithmetic shift for 'negative' number */
        if ( i >= LUA_NBITS ) {
            r = ALLONES;
        }
        else {
            r = trim( ( r >> i ) | ~( trim( ~( lua_Unsigned )0 ) >>
                                      i ) );    /* add signal bit */
        }

        pushunsigned( L, r );
        return 1;
    }
}


static int b_rot( lua_State* L, lua_Integer d ) {
    lua_Unsigned r = checkunsigned( L, 1 );
    int i = d & ( LUA_NBITS - 1 ); /* i = d % NBITS */
    r = trim( r );

    if ( i != 0 ) { /* avoid undefined shift of LUA_NBITS when i == 0 */
        r = ( r << i ) | ( r >> ( LUA_NBITS - i ) );
    }

    pushunsigned( L, trim( r ) );
    return 1;
}


static int b_lrot( lua_State* L ) {
    return b_rot( L, luaL_checkinteger( L, 2 ) );
}


static int b_rrot( lua_State* L ) {
    return b_rot( L, -luaL_checkinteger( L, 2 ) );
}


/*
** get field and width arguments for field-manipulation functions,
** checking whether they are valid.
** ('luaL_error' called without 'return' to avoid later warnings about
** 'width' being used uninitialized.)
*/
static int fieldargs( lua_State* L, int farg, int* width ) {
    lua_Integer f = luaL_checkinteger( L, farg );
    lua_Integer w = luaL_optinteger( L, farg + 1, 1 );
    luaL_argcheck( L, 0 <= f, farg, "field cannot be negative" );
    luaL_argcheck( L, 0 < w, farg + 1, "width must be positive" );

    if ( f + w > LUA_NBITS ) {
        luaL_error( L, "trying to access non-existent bits" );
    }

    *width = ( int )w;
    return ( int )f;
}


static int b_extract( lua_State* L ) {
    int w;
    lua_Unsigned r = trim( checkunsigned( L, 1 ) );
    int f = fieldargs( L, 2, &w );
    r = ( r >> f ) & mask( w );
    pushunsigned( L, r );
    return 1;
}


static int b_replace( lua_State* L ) {
    int w;
    lua_Unsigned r = trim( checkunsigned( L, 1 ) );
    lua_Unsigned v = trim( checkunsigned( L, 2 ) );
    int f = fieldargs( L, 3, &w );
    lua_Unsigned m = mask( w );
    r = ( r & ~( m << f ) ) | ( ( v & m ) << f );
    pushunsigned( L, r );
    return 1;
}


static const luaL_Reg bitlib[] = {
    {"arshift", b_arshift},
    {"band", b_and},
    {"bnot", b_not},
    {"bor", b_or},
    {"bxor", b_xor},
    {"btest", b_test},
    {"extract", b_extract},
    {"lrotate", b_lrot},
    {"lshift", b_lshift},
    {"replace", b_replace},
    {"rrotate", b_rrot},
    {"rshift", b_rshift},
    {NULL, NULL}
};



LUAMOD_API int luaopen_bit32( lua_State* L ) {
    luaL_newlib( L, bitlib );
    return 1;
}


#else                   /* }{ */


LUAMOD_API int luaopen_bit32( lua_State* L ) {
    return luaL_error( L, "library 'bit32' has been deprecated" );
}

#endif                  /* } */
